{
 "cells": [
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Задание 1\n",
    "\n",
    "Печатные газеты использовали свой формат дат для каждого выпуска. Для каждой \n",
    "газеты из списка напишите формат указанной даты для перевода в объект datetime:\n",
    "The Moscow Times - Wednesday, October 2, 2002\n",
    "The Guardian - Friday, 11.10.13\n",
    "Daily News - Thursday, 18 August 1977"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 1,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "2002-10-02 00:00:00\n",
      "2013-10-11 00:00:00\n",
      "1977-08-18 00:00:00\n"
     ]
    }
   ],
   "source": [
    "from datetime import datetime, timedelta\n",
    "\n",
    "KNOWN_FORMATS = [\n",
    "    '%A, %B %d, %Y', #  The Moscow Times - Wednesday, October 2, 2002\n",
    "    '%A, %d.%m.%y',  #  The Guardian - Friday, 11.10.13\n",
    "    '%A, %d %B %Y',  #  Daily News - Thursday, 18 August 1977\n",
    "    '%Y-%m-%d',      #  Формат YYYY-MM-DD\n",
    "]\n",
    "\n",
    "def str2datetime(date):\n",
    "    \"\"\"\n",
    "    Функция для конвертации даты в формате строки в формат datetime. Функция возвращает\n",
    "    дату в формате datetime. Если в переменной str_date указан некорректный формат даты, то функция\n",
    "    выдаст исключение.\n",
    "    date - дата в формате YYYY-MM-DD\n",
    "    \"\"\"\n",
    "    for datetime_format in KNOWN_FORMATS:\n",
    "        try:\n",
    "            return datetime.strptime(date, datetime_format)\n",
    "        except:\n",
    "            pass\n",
    "    raise ValueError('date содержит неподдерживаемый формат времени')\n",
    "    \n",
    "dates = [\n",
    "    'Wednesday, October 2, 2002',\n",
    "    'Friday, 11.10.13',\n",
    "    'Thursday, 18 August 1977'\n",
    "]\n",
    "\n",
    "for date in dates:\n",
    "    print(str2datetime(date))"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Задание 2\n",
    "Дан поток дат в формате YYYY-MM-DD, в которых встречаются некорректные значения:\n",
    "stream = [‘2018-04-02’, ‘2018-02-29’, ‘2018-19-02’]\n",
    "\n",
    "Напишите функцию, которая проверяет эти даты на корректность. Т. е. для каждой \n",
    "даты возвращает True (дата корректна) или False (некорректная дата)."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 2,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "2020-01-01: существует\n",
      "1999-09-29: существует\n",
      "2019-02-35: не существует\n",
      "1995-02-28: существует\n"
     ]
    }
   ],
   "source": [
    "def check_date_format(date):\n",
    "    \"\"\"\n",
    "    Функция для проверки корректности формата времени. В качестве результата выдает True или False.\n",
    "    Корректными считается формат YYYY-MM-DD\n",
    "    date - дата в формате YYYY-MM-DD\n",
    "    \"\"\"\n",
    "    valid_format = '%Y-%m-%d'\n",
    "    try:\n",
    "        datetime.strptime(date, valid_format)\n",
    "        return True\n",
    "    except:\n",
    "        return False\n",
    "    \n",
    "dates = [\n",
    "    '2020-01-01',\n",
    "    '1999-09-29',\n",
    "    '2019-02-35',\n",
    "    '1995-02-28'\n",
    "]   \n",
    "\n",
    "for date in dates:\n",
    "    print(f'{date}:', 'существует' if check_date_format(date) else 'не существует')    "
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Задание 3\n",
    "\n",
    "Напишите функцию date_range, которая возвращает список дат за период от \n",
    "start_date до end_date. Даты должны вводиться в формате YYYY-MM-DD. В случае \n",
    "неверного формата или при start_date > end_date должен возвращаться пустой список."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 8,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "['2020-09-01', '2020-09-02', '2020-09-03', '2020-09-04', '2020-09-05', '2020-09-06', '2020-09-07', '2020-09-08', '2020-09-09', '2020-09-10', '2020-09-11', '2020-09-12']\n"
     ]
    }
   ],
   "source": [
    "def date_range(start_date, end_date):\n",
    "    \"\"\"\n",
    "    Функция для получения списка дат из периода. \n",
    "    start_date - начальная дата в формате YYYY-MM-DD\n",
    "    end_date - конечная дата в формате YYYY-MM-DD\n",
    "    Функция возвращает список дат.\n",
    "    Если формат параметров некорректный, а так же если start_date больше end_date, то функция вернет пустой список.\n",
    "    \"\"\"\n",
    "    \n",
    "    if not check_date_format(start_date) or not check_date_format(end_date) or end_date < start_date:\n",
    "        return []\n",
    "    \n",
    "    start_date = str2datetime(start_date)\n",
    "    end_date = str2datetime(end_date)\n",
    "    \n",
    "    result = []\n",
    "    days_counter = (end_date - start_date).days\n",
    "    \n",
    "    for days in range(0, days_counter+1):\n",
    "        result += [datetime.strftime(start_date + timedelta(days), '%Y-%m-%d')]\n",
    "        \n",
    "    return result\n",
    "    \n",
    "print(date_range('2020-09-01', '2020-09-12')) "
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Задание 4 (бонусное)\n",
    "\n",
    "Ваш коллега прислал код функции:\n",
    "\n",
    "    DEFAULT_USER_COUNT = 3\n",
    "\n",
    "    def delete_and_return_last_user(region, default_list=['A100', 'A101', 'A102']):\n",
    "        \"\"\"\n",
    "        Удаляет из списка default_list последнего пользователя\n",
    "        и возвращает ID нового последнего пользователя.\n",
    "        \"\"\"\n",
    "        element_to_delete = default_list[-1]\n",
    "        default_list.remove(element_to_delete)\n",
    "\n",
    "        return default_list[DEFAULT_USER_COUNT-2]\n",
    "\n",
    "При однократном вызове этой функции все работает корректно:\n",
    "\n",
    "    delete_and_return_last_user(1)\n",
    "    'A101'\n",
    "\n",
    "Однако, при повторном вызове получается ошибка IndexError: list index out of range.\n",
    "\n",
    "**Задание:**\n",
    "\n",
    "Что значит ошибка list index out of range?\n",
    "Почему при первом запуске функция работает корректно, а при втором - нет?"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "**РЕШЕНИЕ:**\n",
    "\n",
    "Ошибка возникает из-за передачи в параметр default_list значения по умолчанию в виде изменяемого типа и который изменяется внутри функции. При первой инициализации в памяти создается изменяемый объект типа list с тремя значениями и к нему прикрепляется ярлык в виде переменной default_list. В результате выполнения функции объект из переменной default_list изменяется. Если вызвать функцию повторно со значением по умолчанию для параметра default_list, то в этом случае объект в памяти повторно не создается и переменная default_list ссылается на объект в памяти, который был изменен в прошлом запуске. А так как у нас в переменной default_list уже не 3 элемента, а 2, то после повторного удаления последнего элемента у нас в default_list находится всего один элемент с индексом 0. А функция пытается найти элемент с индексом DEFAULT_USER_COUNT-2 равным 1. В результате у нас возникает исключение.\n",
    "\n",
    "**КАК РЕШИТЬ ПРОБЛЕМУ:**\n",
    "\n",
    "Если мы передаем в функцию изменяемые объекты, то необходимо создавать копию переменной (поверхностную или глубокую). В нашем примере можно использовать поверхностную копию. В этом случае функция будет работать корректно при повторных вызовах. Также в защитном программировании в таких ситуациях рекомендуется вместо передачи изменяемого объекта по умолчанию передавать None. В этом случае если пользователь сам не указывает объект, то функция по признаку None инициализирует переменную уже внутри функции."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 4,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "A101\n",
      "A101\n",
      "A101\n",
      "A101\n",
      "A101\n",
      "A101\n",
      "A101\n",
      "A101\n",
      "A101\n",
      "A101\n"
     ]
    }
   ],
   "source": [
    "DEFAULT_USER_COUNT = 3\n",
    "\n",
    "def delete_and_return_last_user(region, default_list=None):\n",
    "    \"\"\"\n",
    "    Удаляет из списка default_list последнего пользователя\n",
    "    и возвращает ID нового последнего пользователя.\n",
    "    \"\"\"\n",
    "    if default_list is None:\n",
    "        default_list = ['A100', 'A101', 'A102']\n",
    "        \n",
    "    # Если изменяемый тип остается в значении по умолчанию, то можно сделать поверхностную копию\n",
    "    # default_list = default_list[:]\n",
    "    \n",
    "    element_to_delete = default_list[-1]\n",
    "    default_list.remove(element_to_delete)\n",
    "\n",
    "    return default_list[DEFAULT_USER_COUNT-2]\n",
    "\n",
    "for _ in range(10):\n",
    "    print(delete_and_return_last_user(1))"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": []
  }
 ],
 "metadata": {
  "kernelspec": {
   "display_name": "Python 3",
   "language": "python",
   "name": "python3"
  },
  "language_info": {
   "codemirror_mode": {
    "name": "ipython",
    "version": 3
   },
   "file_extension": ".py",
   "mimetype": "text/x-python",
   "name": "python",
   "nbconvert_exporter": "python",
   "pygments_lexer": "ipython3",
   "version": "3.6.8"
  }
 },
 "nbformat": 4,
 "nbformat_minor": 4
}
